Skip to content

feat(codex): persist reset-credit operation identity - #1526

Draft
luvs01 wants to merge 7 commits into
lidge-jun:devfrom
luvs01:agent/reset-credit-operation-ledger
Draft

feat(codex): persist reset-credit operation identity#1526
luvs01 wants to merge 7 commits into
lidge-jun:devfrom
luvs01:agent/reset-credit-operation-ledger

Conversation

@luvs01

@luvs01 luvs01 commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

Summary

  • require one caller-stable, canonical lowercase UUIDv4 for each explicit reset-credit redemption intent
  • reserve, restore, settle, and short-circuit manual redemptions through a strict synchronous=FULL SQLite ledger before upstream dispatch
  • enforce a consent-bearing management principal: reusable admin-token authority is rejected; GUI sessions and the CLI's exact one-shot capability are admitted
  • keep retry identity across ambiguous response loss: the GUI persists it per account across close/remount, and the CLI retains it in the shared durable SQLite database until a terminal code is observed
  • return the settled terminal code immediately and leave quota refresh to a separate read, avoiding a second ambiguity window after a successful spend
  • retain the dormant generation-aware recovery coordinator as a foundation only; automatic Responses redemption remains disabled pending the separate exhaustion-generation contract in [Feature]: opt-in reset-credit recovery after verified pre-stream quota exhaustion #657

The manual ledger is keyed by a domain-separated hash of the authoritative physical ChatGPT account ID, so main and pool aliases converge without storing credentials or raw physical account IDs. Pending or ambiguous state restores the same operation ID, a same-ID terminal retry returns the recorded code without another upstream call, and only a distinct user redemption intent can replace a settled record.

The storage authority accepts only its exact STRICT, WITHOUT ROWID schema, forbids triggers, validates every bounded row, requires NULL result codes for non-terminal rows, rejects duplicate operation IDs and non-canonical UUIDs, and confirms every mutation by exact in-transaction readback. It atomically migrates only the exact prior recovery-only schema and verifies the legacy table is removed.

The local CLI capability proves an exact process/request/account/operation/endpoint/TTL binding and is one-shot. It is deliberately documented as not cryptographic proof of human presence: the user-consent boundary also relies on the repository's normative rule that agents must not invoke or route around this credit-spending action.

Refs #657.

Test plan

  • Bun 1.3.14: 202 core recovery/ledger/CLI/consent tests passed
  • Bun 1.3.14: 16 focused consume-route tests, 15 GUI tests, and 3 live management-boundary tests passed
  • Bun 1.4.0-canary.1: 20 focused pending-store and consume-route compatibility tests passed
  • TypeScript 7.0.2 tsc --noEmit, privacy scan, GUI lint, docs build, and git diff --check passed
  • independent final security/correctness review found no remaining P0/P1 findings
  • the final two CodeRabbit gaps pass on Bun 1.3.14 and Bun 1.4.0-canary.1 (21 ledger tests each), with typecheck, privacy scan, and git diff --check; PR head is ba17ff255da241ed83de2f7136248558caf9e5cf
  • this branch remains based on 5703473041a9f4f415743652de5d86d51fd66db5; rebasing onto the newer dev intentionally waits for the maintainer product/security decisions below

The focused local suites cover the changed behavior. GitHub Actions remains the authority for the complete cross-platform suite on this exact head.

UI evidence

The GUI change is behavioral only: the existing reset-credit modal retains an opaque retry ID across ambiguous close/remount, blocks dismissal during an in-flight spend, and ignores stale detail responses. It changes no layout, copy, or visual styling. A maintainer gui-screenshot-waived review is requested instead of publishing a local account screenshot.

Maintainer decisions still required

  • Apply maintainer-sponsored only after security review of this exact head; restricted auth surfaces intentionally keep the hygiene gate red until then.
  • Apply gui-screenshot-waived if the behavior-only UI evidence above is sufficient.
  • The durable server ledger currently fails closed after 128 lifetime physical-account identities. Safe terminal-row retirement requires an upstream idempotency replay-window contract; this PR intentionally does not guess one or prune rows unsafely.

Checklist

  • Scope stays focused and avoids unrelated cleanup.
  • Docs were updated for consent authority, durable caller identity, terminal response semantics, and agent restrictions.
  • Security-sensitive changes were reviewed for credential confinement, consent authority, retry durability, ambiguity, schema authority, concurrency, and unsafe defaults.

Review readiness checklist

This PR stays in draft until every box below is ticked. Tick all four boxes once the requirements are met:

  • All CI tests are green on my local testing.

  • I pushed my PR to the latest dev commit.

  • I resolved all correct Codex and CodeRabbit findings.

  • My PR is ready for review.

@github-actions github-actions Bot added enhancement New feature or request intake: hygiene-blocked Deterministic PR hygiene checks failed labels Aug 12, 2026
@github-actions

Copy link
Copy Markdown
Contributor

⚠️ Deterministic hygiene checks failed.

  • unsponsored_surface — This changes an authentication, workflow, release-automation, or dependency surface. MAINTAINERS.md requires security review for these; ask a maintainer to apply maintainer-sponsored once they have reviewed it. Paths: src/codex/auth-api.ts.

@github-actions

github-actions Bot commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

⏳ DRAFT

  • UI screenshot required. hygiene: unsponsored_surface.

What to do

  • Add a screenshot of the UI change to the PR description.
  • Fix unsponsored_surface — This changes an authentication, workflow, release-automation, or dependency surface. MAINTAINERS.md requires security review for these; ask a maintainer to apply maintainer-sponsored once they have reviewed it. Paths: src/cli/account-auth.ts, src/codex/auth-api.ts, src/server/management-api.ts, src/server/management-auth.ts.
  • Tick all four boxes in the PR description once you're done (currently 1/4).

Review readiness checklist

  • ⬜ All CI tests are green on my local testing.
  • ⬜ I pushed my PR to the latest dev commit.
  • ✅ I resolved all correct Codex and CodeRabbit findings.
  • ⬜ My PR is ready for review.

1/4 boxes ticked.

This pull request was already a draft. Its draft status will be preserved after every issue above is resolved.
@luvs01 Tick the boxes once your local CI is green, your branch is on the latest dev commit, and every correct Codex and CodeRabbit finding is resolved.

@luvs01

luvs01 commented Aug 12, 2026

Copy link
Copy Markdown
Contributor Author

@CodeRabbit review

@coderabbitai

coderabbitai Bot commented Aug 12, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai

coderabbitai Bot commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

The reset-credit flow now uses a typed upstream client, a durable SQLite operation ledger, stable UUIDv4 operation IDs, and operation-specific recovery. The API supports idempotent retries, single-flight contention handling, explicit cancellation responses, and refreshed usage reporting.

Changes

Reset-credit consumption and recovery

Layer / File(s) Summary
Durable operation ledger
src/codex/reset-credit-operation-ledger.ts, src/config.ts, tests/codex-reset-credit-operation-ledger.test.ts
Adds schema validation and migration, transactional manual-operation reservations, ownership checks, settlement states, ambiguity handling, capacity limits, and database write-path preparation.
Operation-specific recovery
src/codex/reset-credit-recovery.ts
Exports UUIDv4 operation-ID validation and generation comparison. The coordinator can create logical turns from reserved operation IDs.
Reset-credit consume client
src/codex/reset-credit-consume.ts, tests/codex-reset-credit-consume.test.ts
Adds authenticated POST handling, timeout and abort processing, bounded response parsing, typed errors, accepted result codes, and frozen results.
Auth API integration
src/codex/auth-api.ts, tests/codex-auth-api.test.ts
Uses the typed client and manual ledger for idempotency. The route handles terminal retries, single-flight contention, usage refresh, HTTP 499 cancellation responses, upstream-status errors, and HTTP 502 invalid-response errors.
Stable client operation IDs
gui/src/components/CodexAccountPool.tsx, gui/src/components/codex-account-pool-handlers.ts, gui/src/lib/uuid.ts, gui/src/pages/claude-code-types.ts, src/cli/account-auth.ts, gui/tests/*, tests/cli-account.test.ts
Browser and CLI clients send UUIDv4 operation IDs. The GUI reuses one ID across reset retries and clears it when the popup closes or redemption completes.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant AuthAPI
  participant OperationLedger
  participant consumeCodexResetCredit
  participant CodexUpstream
  Client->>AuthAPI: account ID and stable operation ID
  AuthAPI->>OperationLedger: open manual reset-credit operation
  OperationLedger-->>AuthAPI: reservation or terminal result
  AuthAPI->>consumeCodexResetCredit: credentials and reserved operation ID
  consumeCodexResetCredit->>CodexUpstream: authenticated consume request
  CodexUpstream-->>consumeCodexResetCredit: consume result
  consumeCodexResetCredit-->>AuthAPI: validated result or typed error
  AuthAPI->>OperationLedger: settle or mark operation ambiguous
  AuthAPI-->>Client: reset result or structured error
Loading

Possibly related PRs

  • lidge-jun/opencodex#475: Both changes modify redeemResetCredit and the reset-credit consume request contract.
  • lidge-jun/opencodex#1410: This change extends its reset-credit recovery infrastructure with manual consumption and ledger-backed idempotency.

Suggested labels: maintainer-sponsored

Suggested reviewers: ingwannu, lidge-jun, wibias

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 14.58% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main change: durable persistence of reset-credit operation identity.
✨ Finishing Touches 💡 1
🛠️ Fix failing CI checks 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 15

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/codex/auth-api.ts`:
- Around line 1775-1780: In src/codex/auth-api.ts:1775-1780, integrate
CodexResetCreditRecoveryGeneration with openResetCreditOperation before
consumeCodexResetCredit, reuse the reserved operationId, return stored terminal
outcomes without dispatching upstream, settle terminal results, and mark
ambiguous transport failures while preserving fail-closed state. In
tests/codex-auth-api.test.ts:2198-2201, add an ambiguous-transport retry case
asserting only one upstream consume and reuse of the same redeem_request_id.

In `@src/codex/reset-credit-operation-ledger.ts`:
- Around line 389-391: In src/codex/reset-credit-operation-ledger.ts lines
389-391, update the catch handler to capture the error and warn for every cause
except SQLITE_BUSY, using only the ledger’s own error strings; apply the same
change to the second handler at lines 425-427. In src/config.ts lines 2414-2422,
enforce the documented precondition in the config-mutation flow by throwing when
configMutationLockDepth > 0, so nested mutations surface as errors.
- Around line 430-464: Document the public throw-versus-return contract for
markResetCreditOperationAmbiguous, openResetCreditOperation, and
settleResetCreditOperation. State that invalid now values and invalid
generations throw TypeError, while invalid operation IDs or non-terminal settle
codes return the existing mismatch result, keeping the current behavior
unchanged.
- Around line 15-20: Replace TERMINAL_CODES with an exhaustive
Record<CodexResetCreditConsumeCode, "confirmed" | "stopped">, assigning each
consume code its terminal state so future enum members require compiler updates.
Update terminal membership checks and state validation in
settleResetCreditOperation, including the mappings around lines 165-166 and 459,
to derive behavior from this record rather than set membership.
- Around line 282-293: Reuse the existing compareGenerationOrder comparator in
compareGeneration instead of maintaining duplicate lexicographic ordering logic.
Import or otherwise expose compareGenerationOrder as needed, and preserve the
current credentialGeneration-then-exhaustionGeneration ordering contract.
- Around line 197-208: Update the table_xinfo query in the reset-credit
operation ledger validation to interpolate or otherwise use the existing
TABLE_NAME constant instead of the hardcoded reset_credit_operations identifier,
while preserving the current column retrieval and validation flow.
- Around line 305-306: Update the withLedger callback type to reject inferred
PromiseLike results at compile time, while preserving synchronous return values
and the existing runtime guard. Ensure async callbacks and direct promise
returns fail type checking rather than reaching the surrounding catch path.
- Around line 57-67: Add a regression test in the reset credit operation ledger
test suite that creates a fresh table and asserts its main.sqlite_schema.sql
equals the EXPECTED_SCHEMA_SQL value derived from CREATE_TABLE.replace("main.",
""). Reuse the existing table-opening/setup path and verify the schema contract
before exercising openResetCreditOperation.

In `@src/codex/reset-credit-recovery.ts`:
- Around line 487-493: Make createLogicalTurnForOperation require a branded
reserved-operation-id type rather than a plain string, while retaining its
existing UUID validation and restoration behavior. Define the brand near the
recovery types and expose a controlled conversion usable only by
openResetCreditOperation; return the branded value from that function’s execute
and terminal results so arbitrary UUIDs cannot reach the restoration seam
without a ledger-issued token.

In `@src/config.ts`:
- Around line 2414-2422: Enforce the documented non-nesting precondition in
prepareConfigMutationDatabasePathForWrite by checking configMutationLockDepth
and throwing a clear error when called while withConfigMutationLockSync is
active; otherwise preserve the existing database-path return. Ensure the related
withLedger error-reporting path surfaces this specific failure rather than
leaving it indistinguishable from ordinary SQLITE_BUSY contention.

In `@tests/codex-reset-credit-operation-ledger.test.ts`:
- Around line 234-242: Move the holder.exec call that sets busy_timeout and
begins the transaction inside the existing try block so holder.close() is always
reached via finally. Because ROLLBACK can fail when BEGIN IMMEDIATE did not open
a transaction, guard or otherwise suppress rollback errors while ensuring
holder.close() still executes.
- Around line 37-63: Add a focused regression test near the existing
duplicate-ledger tests that uses the canonical schema, creates a reservation for
one account, inserts the same operationId under a different account, and
verifies subsequent openResetCreditOperation calls fail closed with unavailable.
Keep createLaxDuplicateLedger for schema-validation coverage and exercise
initializeTable’s cross-account operationIds rejection path.
- Around line 105-112: Add an assertion to the recovery test around
coordinator.recover and settleResetCreditOperation that explicitly verifies the
durable ledger remains pending after recovery while terminalByAccount contains
the terminal fence, documenting this intentional boundary without changing
production behavior. Use the existing ledger lookup and coordinator state
symbols rather than adding new flows.
- Around line 289-304: Update the overflow-row setup in the test to derive its
operation_id from MAX_RESET_CREDIT_OPERATION_ACCOUNTS using a value beyond the
loop’s generated index range, rather than hardcoding a potentially colliding
suffix. Add a concise comment documenting that this over-capacity test relies on
SELECT_ALL fetching MAX_RESET_CREDIT_OPERATION_ACCOUNTS + 1 rows so the capacity
check is exercised.
- Around line 24-35: Update corruptFirstRecord to remove the LIMIT 1 clause from
its UPDATE statement, leaving the existing single-row test setup and
databasePath implementation unchanged.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 203e3b01-da12-407a-8b20-1221513b02b8

📥 Commits

Reviewing files that changed from the base of the PR and between 1b71471 and bfa47f9.

📒 Files selected for processing (8)
  • src/codex/auth-api.ts
  • src/codex/reset-credit-consume.ts
  • src/codex/reset-credit-operation-ledger.ts
  • src/codex/reset-credit-recovery.ts
  • src/config.ts
  • tests/codex-auth-api.test.ts
  • tests/codex-reset-credit-consume.test.ts
  • tests/codex-reset-credit-operation-ledger.test.ts

Comment thread src/codex/auth-api.ts Outdated
Comment thread src/codex/reset-credit-operation-ledger.ts Outdated
Comment thread src/codex/reset-credit-operation-ledger.ts Outdated
Comment thread src/codex/reset-credit-operation-ledger.ts
Comment thread src/codex/reset-credit-operation-ledger.ts
Comment thread tests/codex-reset-credit-operation-ledger.test.ts
Comment thread tests/codex-reset-credit-operation-ledger.test.ts
Comment thread tests/codex-reset-credit-operation-ledger.test.ts
Comment thread tests/codex-reset-credit-operation-ledger.test.ts
Comment thread tests/codex-reset-credit-operation-ledger.test.ts
@Ingwannu

Copy link
Copy Markdown
Owner

This is not ready for maintainer sponsorship or integration review on the current head.

The PR describes a durable operation ledger, but the production manual route in src/codex/auth-api.ts still calls consumeCodexResetCredit() with a fresh crypto.randomUUID() and does not call openResetCreditOperation(), restore the reserved operation into the recovery coordinator, settle terminal outcomes, or mark ambiguous transport failures. In other words, the new durability mechanism is currently dormant even for the only production consumer, while the PR changes the transport semantics and adds a large persistence authority.

That mismatch matters: after an ambiguous consume, a retry can still mint a new redemption ID, so the central idempotency property this foundation claims is not exercised end to end. CodeRabbit's first blocker identifies the same missing integration and should be treated as valid. The remaining ledger schema/transaction findings also need individual resolution before a security review.

Please keep this draft and do not request maintainer-sponsored yet. First integrate the ledger into the manual route with exact operation-ID reuse, terminal short-circuiting, ambiguous marking, and settlement tests; resolve the valid strict-schema/transaction findings; then rebase and request a full credential/persistence security review on the exact head. I am not applying a label, approving, or merging this version.

@luvs01
luvs01 force-pushed the agent/reset-credit-operation-ledger branch 2 times, most recently from d089c8a to aefc06f Compare August 12, 2026 11:12
@luvs01

luvs01 commented Aug 12, 2026

Copy link
Copy Markdown
Contributor Author

Implemented the requested durable manual-route integration on exact head a89e331b94.

  • the GUI and CLI now send one caller-stable UUIDv4 operation ID; the API rejects missing or malformed IDs
  • the manual route reserves/restores that ID before dispatch, resumes pending or ambiguous outcomes across retries/restarts, short-circuits terminal outcomes, marks post-dispatch failures ambiguous, and settles durably before returning success
  • the manual namespace uses a hashed authoritative physical ChatGPT account identity, so both main and pool aliases are covered without fabricating an automatic-recovery generation
  • the strict schema/transaction, branded restoration, duplicate-ID, bounded-read, migration, and cleanup findings are addressed; all 15 review threads now have exact commit evidence and are resolved
  • automatic Responses redemption remains inactive and still requires the separately proven exhaustion-generation adapter tracked by [Feature]: opt-in reset-credit recovery after verified pre-stream quota exhaustion #657

Focused verification passed on Bun 1.3.14 and 1.4.0-canary.1 (45 affected regressions on each), TypeScript passed on both, privacy scan and diff check passed, and two independent read-only reviews found no remaining actionable P0-P3 findings. I did not duplicate the full GitHub CI workload locally.

This branch is based on current dev; the latest non-overlapping rebase preserved all 17 changed-file blobs byte-for-byte. The remaining gates require maintainer action: security review / maintainer-sponsored, workflow approval for exact-head GitHub Actions, and gui-screenshot-waived because the GUI change only retains an opaque retry ID and has no visual change. Requesting the exact-head credential/persistence review you asked for.

@luvs01
luvs01 force-pushed the agent/reset-credit-operation-ledger branch 3 times, most recently from 93a0bd6 to e2218de Compare August 12, 2026 17:38
@luvs01

luvs01 commented Aug 12, 2026

Copy link
Copy Markdown
Contributor Author

Exact-head handoff for e2218de on current dev c125b61: the rebase preserved all 17 changed-file blobs byte-for-byte. Focused verification passed on Bun 1.3.14 and Bun 1.4 (32 core contract tests and 13 GUI tests per runtime), TypeScript checks passed on both runtimes, privacy scans passed on both runtimes, and diff-check is clean. All review threads remain resolved. The full local suite was not duplicated because GitHub CI owns that workload. The GUI change is behavioral only and changes no layout, copy, or visual state, so please review this exact head for maintainer-sponsored and gui-screenshot-waived, then approve exact-head GitHub Actions when appropriate.

@Wibias
Wibias marked this pull request as ready for review August 12, 2026 20:34
@github-actions
github-actions Bot marked this pull request as draft August 12, 2026 20:37
@Wibias
Wibias marked this pull request as ready for review August 12, 2026 20:40
@github-actions
github-actions Bot marked this pull request as draft August 12, 2026 20:40

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/codex/reset-credit-recovery.ts (1)

489-510: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Move the UUID check into registerLogicalTurn so both entry points share one invariant.

createLogicalTurnForOperation validates its input at Lines 500-502. createLogicalTurn does not validate the value from crypto.randomUUID() at Line 490. registerLogicalTurn at Line 506 accepts a plain string and performs no check, so the shared private path no longer enforces the identity contract that both public methods depend on.

The durable ledger takes the opposite position for the same value: openResetCreditOperation in src/codex/reset-credit-operation-ledger.ts Line 562 asserts isCodexResetCreditOperationId(randomUUID()) and throws "runtime generated invalid UUID". Two producers of the same operation identity now apply different levels of trust to the same runtime API.

Enforce the invariant once, at the single registration seam:

♻️ Proposed refactor
   createLogicalTurn(): CodexResetCreditLogicalTurn {
     const operationId = crypto.randomUUID();
     return this.registerLogicalTurn(operationId);
   }
 
   /**
    * Restores a logical turn whose operation identity was durably reserved before
    * this coordinator instance existed. Only a validated ledger/adapter should use
    * this seam; ordinary requests must keep using createLogicalTurn().
    */
   createLogicalTurnForOperation(operationId: CodexReservedOperationId): CodexResetCreditLogicalTurn {
-    if (!isCodexResetCreditOperationId(operationId)) {
-      throw new TypeError("operationId must be an RFC 4122 version 4 UUID");
-    }
     return this.registerLogicalTurn(operationId);
   }
 
   private registerLogicalTurn(operationId: string): CodexResetCreditLogicalTurn {
+    if (!isCodexResetCreditOperationId(operationId)) {
+      throw new TypeError("operationId must be an RFC 4122 version 4 UUID");
+    }
     const turn = Object.freeze({ operationId });

The existing assertion at tests/codex-reset-credit-operation-ledger.test.ts Lines 264-265 keeps passing, because the thrown message is unchanged.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/codex/reset-credit-recovery.ts` around lines 489 - 510, Move the
isCodexResetCreditOperationId validation and existing TypeError message from
createLogicalTurnForOperation into the shared private registerLogicalTurn
method, applying it before the turn is stored. Remove the duplicate check from
createLogicalTurnForOperation so both createLogicalTurn and
createLogicalTurnForOperation enforce the same operation identity invariant
through registerLogicalTurn.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/codex/auth-api.ts`:
- Around line 1822-1858: Replace every inline server-busy response construction
in the route, including the branches around openManualResetCreditOperation,
settleManualResetCreditOperation, and the catch block, with calls to the
existing manualResetCreditBusyResponse() helper. Preserve the current return
behavior at each site and use the helper for the complete 503 response contract.

In `@src/codex/reset-credit-operation-ledger.ts`:
- Around line 513-520: Replace the literal error.message comparison in
warnLedgerUnavailable with a shared error identity from config.ts: export a
dedicated error class or message constant at the existing nested-mutation throw
site, then import and use it here. Preserve the current nested diagnostic and
generic unavailable log behavior, without logging sensitive data.
- Around line 245-251: Update the validation logic around terminalState in the
record parser so non-terminal states require code to be null, rejecting any
unrecognized or non-null code instead of dropping it. Preserve the existing
terminal-state mapping and validation behavior, and add a focused malformed-row
regression beside the existing tests for this ledger subsystem.
- Around line 726-753: Make the terminal current-record/new-operationId path
explicit in the manual operation decision logic: retain the execute return for
non-terminal records, the terminal return for same-operation retries, and
explicitly return a fresh non-resumed execute result for a distinct operationId
after a terminal record. Update the corresponding record replacement flow as
needed, and add a focused regression beside the existing manual tests covering
settlement of one operationId followed by a distinct operationId for the same
account, asserting execute with resumed false and the new id.

In `@src/codex/reset-credit-recovery.ts`:
- Around line 37-42: Enforce canonical lowercase IDs in
CODEX_RESET_CREDIT_OPERATION_ID_PATTERN by removing case-insensitive matching,
so isCodexResetCreditOperationId rejects uppercase hexadecimal values before
ledger operations. Add coverage for an uppercase terminal ID followed by a
lowercase retry, preserving case-sensitive behavior in
SELECT_KEY_BY_OPERATION_ID, UPDATE_RECORD, and updateOperation.

In `@tests/codex-reset-credit-operation-ledger.test.ts`:
- Around line 107-123: Extend the migration test around migrateLegacyTable to
assert the reopened reset_credit_operations schema exactly matches
RESET_CREDIT_OPERATION_SCHEMA_SQL_FOR_TESTS and that
reset_credit_operations_legacy_v1 no longer exists. Keep the existing
migrated-row assertions, and use the canonical schema/absence checks to cover
both structural migration output and legacy-table cleanup.

---

Outside diff comments:
In `@src/codex/reset-credit-recovery.ts`:
- Around line 489-510: Move the isCodexResetCreditOperationId validation and
existing TypeError message from createLogicalTurnForOperation into the shared
private registerLogicalTurn method, applying it before the turn is stored.
Remove the duplicate check from createLogicalTurnForOperation so both
createLogicalTurn and createLogicalTurnForOperation enforce the same operation
identity invariant through registerLogicalTurn.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 073cef52-36eb-4d5d-a865-a84889131ec6

📥 Commits

Reviewing files that changed from the base of the PR and between bfa47f9 and e2218de.

📒 Files selected for processing (15)
  • gui/src/components/CodexAccountPool.tsx
  • gui/src/components/codex-account-pool-handlers.ts
  • gui/src/lib/uuid.ts
  • gui/src/pages/claude-code-types.ts
  • gui/tests/browser-uuid.test.ts
  • gui/tests/codex-account-pool-handlers.test.ts
  • gui/tests/codex-account-pool-toast-tone.test.tsx
  • src/cli/account-auth.ts
  • src/codex/auth-api.ts
  • src/codex/reset-credit-operation-ledger.ts
  • src/codex/reset-credit-recovery.ts
  • src/config.ts
  • tests/cli-account.test.ts
  • tests/codex-auth-api.test.ts
  • tests/codex-reset-credit-operation-ledger.test.ts

Comment thread src/codex/auth-api.ts
Comment thread src/codex/reset-credit-operation-ledger.ts
Comment thread src/codex/reset-credit-operation-ledger.ts
Comment thread src/codex/reset-credit-operation-ledger.ts
Comment thread src/codex/reset-credit-recovery.ts
Comment thread tests/codex-reset-credit-operation-ledger.test.ts

@Wibias Wibias left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Exact-head review on e2218de6c: requesting changes before security sponsorship.

The ledger/transport hardening is materially improved and I did not find an auth-token leak, SSRF path, unbounded upstream-body parse, or fail-open SQLite corruption path in the changed implementation. However, there is one remaining security-boundary blocker on the reset-credit consume authorization: this operation spends a scarce user-owned credit, so it needs the repository's existing consent-bearing authorization boundary rather than ordinary management admission plus client-side confirmation. Please thread an appropriate ManagementPrincipal / purpose-built consent capability through the /api/codex-auth/reset-credits/consume path and add regression coverage that distinguishes ordinary admin-token admission from the intended consent-bearing path. A body/header confirmed flag is not sufficient evidence of consent.

I am intentionally keeping the detailed attack-path notes out of the public PR per the repository's security-working-note policy. Please do not apply maintainer-sponsored on this head. After the security fix, rebase onto current dev and request an exact-head re-review, since dev has moved since this branch was cut.

@Wibias Wibias left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Full security re-review on exact head e2218de6ce25d18ee7386b9fd2cf4ed979a159e6 (unchanged since my previous review): changes are still required.

The security-boundary blocker remains. /api/codex-auth/reset-credits/consume spends a scarce user-owned credit, but the route still executes after ordinary management admission without requiring the repository's consent-bearing principal/capability. The management layer already distinguishes admin-token from gui-session, specifically because a local coding agent can read the admin token; the legacy Codex-auth dispatch does not carry that principal into handleCodexAuthAPI, and the consume route therefore cannot enforce the intended consent boundary. Please thread an appropriate ManagementPrincipal or purpose-built consent capability through this path and add a regression proving ordinary admin-token admission cannot authorize the spend while the intended consent-bearing path can. The CLI's client-side --yes is useful UX but cannot by itself turn a raw admin-token request into server-verifiable consent.

I completed a diff-scoped security pass across all 17 changed files plus the supporting management-auth boundary. I did not find a new credential disclosure, attacker-controlled SSRF destination, unbounded upstream response-body parse, or independently exploitable SQLite fail-open path. The current uppercase UUID/case-sensitivity finding is a real idempotency/correctness defect and should be fixed, but it is not a separate authorization vulnerability: an already-authorized caller can always choose a genuinely new operation ID. The non-terminal code validation finding is likewise valid fail-closed hardening, but I did not find an external changed input path that can create that malformed durable row without local database write access.

There are also unresolved CodeRabbit findings on this head, including canonical lowercase operation IDs and strict non-terminal row validation. Please resolve the valid findings before requesting sponsorship.

Integration is now a separate blocker: current dev is ac58e0e2a54eda6a593f88287f423a38fd05dff8, 34 commits ahead of this head, and GitHub currently reports the PR non-mergeable. The intervening changes overlap src/codex/auth-api.ts, src/config.ts, and related tests, so this needs a real rebase/conflict resolution rather than freshness being treated as bookkeeping. This exact head previously had a successful Cross-platform CI run and successful React Doctor runs, but the latest React Doctor run is action_required with no jobs. After the security/correctness fixes and rebase, rerun the full maintained matrix on the new exact head.

I am keeping detailed attack-path notes out of the public review per the repository's security-working-note policy. Please do not apply maintainer-sponsored on this head.

@Ingwannu Ingwannu left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The durable-idempotency direction is valuable, but exact head e2218de has a reproduced duplicate-consume path, so I am requesting changes.

CODEX_RESET_CREDIT_OPERATION_ID_PATTERN accepts uppercase UUID hex, while the ledger stores and compares operation IDs case-sensitively. I reproduced the boundary directly on this head:

  • reserve AAAAAAAA-AAAA-4AAA-8AAA-AAAAAAAAAAAA -> execute, resumed false
  • settle it as already_redeemed -> updated
  • retry the same UUID in lowercase -> execute, resumed false

That terminal retry is authorized as a new irreversible consume instead of returning the stored terminal result. Enforce one canonical representation before any ledger lookup/write (prefer rejecting non-lowercase UUIDs or normalize at the API boundary and store only the canonical value), and add both ledger-level and management-route regressions for an uppercase/lowercase retry pair.

The PR is also currently 47 commits behind dev, conflicted, draft, and the latest CodeRabbit pass still has unresolved ledger findings. Please resolve the valid findings, rebase onto current dev, keep the GUI change behavior-only, and rerun the focused suite plus typecheck, privacy scan, and exact-head cross-platform CI before requesting sponsorship/security approval. I am not applying maintainer-sponsored on this head.

@luvs01
luvs01 force-pushed the agent/reset-credit-operation-ledger branch from e2218de to 6b067d3 Compare August 13, 2026 08:18
@luvs01
luvs01 force-pushed the agent/reset-credit-operation-ledger branch from 6b067d3 to 463a06c Compare August 13, 2026 08:37
@luvs01
luvs01 force-pushed the agent/reset-credit-operation-ledger branch from 463a06c to cefac33 Compare August 13, 2026 08:38
@luvs01

luvs01 commented Aug 13, 2026

Copy link
Copy Markdown
Contributor Author

@Wibias @Ingwannu 요청하신 변경 사항은 PR 기준 dev base (5703473041a9f4f415743652de5d86d51fd66db5)의 exact head ba17ff255da241ed83de2f7136248558caf9e5cf에서 처리했습니다.

  • reusable admin-token은 reset-credit 소비를 시작할 수 없으며, gui-session 또는 account/operation/process/endpoint/TTL에 묶인 one-shot CLI capability만 허용합니다. 이 capability는 human presence의 암호학적 증명이 아니라는 한계도 코드와 문서에 명시했습니다.
  • GUI는 ambiguous 응답 뒤 모달 close/remount에서도 계정별 동일 UUID를 재사용하고, in-flight dismissal 및 stale async result를 차단합니다.
  • CLI는 pending UUID를 synchronous=FULL shared SQLite에 terminal 응답까지 보존하므로 Windows crash/response-loss 뒤에도 새 UUID를 발급하지 않습니다.
  • 서버는 durable settlement 직후 terminal {code}를 반환하고 quota refresh를 후속 read로 분리해, 소진 성공 뒤 응답 유실/refresh 실패로 생기던 두 번째 ambiguity window를 제거했습니다.
  • 기존 unresolved review findings도 canonical lowercase UUID, non-terminal code IS NULL, shared busy response/error identity, explicit terminal/new-intent branch, exact migration schema/legacy drop, common logical-turn UUID validation과 회귀 테스트로 반영했습니다.

검증은 Bun 1.3.14 core 202개, consume-route 16개, GUI 15개, live management boundary 3개와 Bun 1.4 focused 20개가 모두 통과했고, TypeScript, privacy scan, GUI lint, docs build, diff check도 통과했습니다. 추가로 terminal/new-intent 분기를 명시화하고 uppercase-terminal→lowercase-retry 회귀를 보강했으며, 양 Bun에서 ledger 21개와 typecheck/privacy/diff check가 통과했습니다. 독립 최종 검토에서도 남은 P0/P1은 없었습니다.

따라서 현재 코드 측 변경 요청은 처리 완료됐으며, 남은 사항은 유지관리자 결정입니다.

  1. exact head 보안 검토 후 maintainer-sponsored 적용 여부
  2. behavior-only GUI 변경에 대한 gui-screenshot-waived 적용 여부
  3. durable ledger의 lifetime 128 physical-account fail-closed 한도에 대해 upstream idempotency replay-window 계약을 정한 뒤 안전한 retirement 정책을 둘지 여부 (계약 없이 terminal row를 임의 삭제하면 중복 소진 위험이 있어 이번 변경에서는 prune하지 않았습니다)

PR은 이 결정과 exact-head CI 확인이 끝날 때까지 draft로 유지합니다.

@luvs01
luvs01 requested review from Ingwannu and Wibias August 13, 2026 08:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request intake: hygiene-blocked Deterministic PR hygiene checks failed

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants